﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Cci;
using Microsoft.Cci.MutableCodeModel;

namespace Mutator
{
    class Pair
    {
        public IExpression original;
        public IExpression mutated;
        public Pair(IExpression o, IExpression m)
        {
            original = o;
            mutated = m;
        }

    }
    class ExpressionMutantGenerator : Microsoft.Cci.MutableCodeModel.MethodBodyCodeMutator
    {
        public List<Pair> list = new List<Pair>();
        
        Random rand = new Random();
        public ExpressionMutantGenerator(IMetadataHost host)
            : base(host)
        {

        }
        public ExpressionMutantGenerator(IMetadataHost host, ISourceLocationProvider/*?*/ sourceLocationProvider)
            : base(host, sourceLocationProvider)
        {

        }
        
        public override IExpression Visit(Multiplication multiplication)
        {
            Console.WriteLine("++++++ multiplication");

            BinaryOperation newOp1 = new Addition();
            newOp1.LeftOperand = multiplication.LeftOperand;
            newOp1.RightOperand = multiplication.RightOperand;

            BinaryOperation newOp2 = new Subtraction();
            newOp2.LeftOperand = multiplication.LeftOperand;
            newOp2.RightOperand = multiplication.RightOperand;

            BinaryOperation newOp3 = new Modulus();
            newOp3.LeftOperand = multiplication.LeftOperand;
            newOp3.RightOperand = multiplication.RightOperand;

            BinaryOperation newOp4 = new Division();
            newOp4.LeftOperand = multiplication.LeftOperand;
            newOp4.RightOperand = multiplication.RightOperand;

            
            list.Add(new Pair(multiplication, newOp1));
            list.Add(new Pair(multiplication, newOp2));
            list.Add(new Pair(multiplication, newOp3));
            list.Add(new Pair(multiplication, newOp4));
            return this.Visit((BinaryOperation)multiplication);
        }
        public override IExpression Visit(Division division)
        {
            Console.WriteLine("++++++ division");
            BinaryOperation newOp1 = new Addition();
            newOp1.LeftOperand = division.LeftOperand;
            newOp1.RightOperand = division.RightOperand;

            BinaryOperation newOp2 = new Subtraction();
            newOp2.LeftOperand = division.LeftOperand;
            newOp2.RightOperand = division.RightOperand;

            BinaryOperation newOp3 = new Modulus();
            newOp3.LeftOperand = division.LeftOperand;
            newOp3.RightOperand = division.RightOperand;

            BinaryOperation newOp4 = new Multiplication();
            newOp4.LeftOperand = division.LeftOperand;
            newOp4.RightOperand = division.RightOperand;


            list.Add(new Pair(division, newOp1));
            list.Add(new Pair(division, newOp2));
            list.Add(new Pair(division, newOp3));
            list.Add(new Pair(division, newOp4));
            return this.Visit((BinaryOperation)division);
        }
        public override IExpression Visit(Addition addition)
        {
            Console.WriteLine("++++++ addition");
            BinaryOperation newOp1 = new Multiplication();
            newOp1.LeftOperand = addition.LeftOperand;
            newOp1.RightOperand = addition.RightOperand;

            BinaryOperation newOp2 = new Subtraction();
            newOp2.LeftOperand = addition.LeftOperand;
            newOp2.RightOperand = addition.RightOperand;

            BinaryOperation newOp3 = new Modulus();
            newOp3.LeftOperand = addition.LeftOperand;
            newOp3.RightOperand = addition.RightOperand;

            BinaryOperation newOp4 = new Division();
            newOp4.LeftOperand = addition.LeftOperand;
            newOp4.RightOperand = addition.RightOperand;


            list.Add(new Pair(addition, newOp1));
            list.Add(new Pair(addition, newOp2));
            list.Add(new Pair(addition, newOp3));
            list.Add(new Pair(addition, newOp4));
            return this.Visit((BinaryOperation)addition);
        }
        public override IExpression Visit(Subtraction subtraction)
        {
            Console.WriteLine("++++++ subtraction");
            BinaryOperation newOp1 = new Addition();
            newOp1.LeftOperand = subtraction.LeftOperand;
            newOp1.RightOperand = subtraction.RightOperand;

            BinaryOperation newOp2 = new Multiplication();
            newOp2.LeftOperand = subtraction.LeftOperand;
            newOp2.RightOperand = subtraction.RightOperand;

            BinaryOperation newOp3 = new Modulus();
            newOp3.LeftOperand = subtraction.LeftOperand;
            newOp3.RightOperand = subtraction.RightOperand;

            BinaryOperation newOp4 = new Division();
            newOp4.LeftOperand = subtraction.LeftOperand;
            newOp4.RightOperand = subtraction.RightOperand;


            list.Add(new Pair(subtraction, newOp1));
            list.Add(new Pair(subtraction, newOp2));
            list.Add(new Pair(subtraction, newOp3));
            list.Add(new Pair(subtraction, newOp4));
            return this.Visit((BinaryOperation)subtraction);
        }
        public override IExpression Visit(Modulus modulus)
        {
            Console.WriteLine("++++++ modulus");
            BinaryOperation newOp1 = new Addition();
            newOp1.LeftOperand = modulus.LeftOperand;
            newOp1.RightOperand = modulus.RightOperand;

            BinaryOperation newOp2 = new Subtraction();
            newOp2.LeftOperand = modulus.LeftOperand;
            newOp2.RightOperand = modulus.RightOperand;

            BinaryOperation newOp3 = new Multiplication();
            newOp3.LeftOperand = modulus.LeftOperand;
            newOp3.RightOperand = modulus.RightOperand;

            BinaryOperation newOp4 = new Division();
            newOp4.LeftOperand = modulus.LeftOperand;
            newOp4.RightOperand = modulus.RightOperand;


            list.Add(new Pair(modulus, newOp1));
            list.Add(new Pair(modulus, newOp2));
            list.Add(new Pair(modulus, newOp3));
            list.Add(new Pair(modulus, newOp4));
            return this.Visit((BinaryOperation)modulus);
        }
        
        public override IExpression Visit(BitwiseAnd bitwiseAnd)
        {
            Console.WriteLine("++++++ bitwiseand");
            BinaryOperation newOp1 = new BitwiseOr();
            newOp1.RightOperand = bitwiseAnd.RightOperand;
            newOp1.LeftOperand = bitwiseAnd.LeftOperand;

            BinaryOperation newOp2 = new ExclusiveOr();
            newOp2.RightOperand = bitwiseAnd.RightOperand;
            newOp2.LeftOperand = bitwiseAnd.LeftOperand;


            list.Add(new Pair(bitwiseAnd, newOp1));
            list.Add(new Pair(bitwiseAnd, newOp2));

            return this.Visit((BinaryOperation)bitwiseAnd);
        }
        public override IExpression Visit(BitwiseOr bitwiseOr)
        {
            Console.WriteLine("++++++ bitwiseor");
            BinaryOperation newOp1 = new BitwiseAnd();
            newOp1.RightOperand = bitwiseOr.RightOperand;
            newOp1.LeftOperand = bitwiseOr.LeftOperand;

            BinaryOperation newOp2 = new ExclusiveOr();
            newOp2.RightOperand = bitwiseOr.RightOperand;
            newOp2.LeftOperand = bitwiseOr.LeftOperand;


            list.Add(new Pair(bitwiseOr, newOp1));
            list.Add(new Pair(bitwiseOr, newOp2));

            return this.Visit((BinaryOperation)bitwiseOr);
        }
        public override IExpression Visit(ExclusiveOr exclusiveOr)
        {
            Console.WriteLine("++++++ exclusiveor");
            BinaryOperation newOp1 = new BitwiseOr();
            newOp1.RightOperand = exclusiveOr.RightOperand;
            newOp1.LeftOperand = exclusiveOr.LeftOperand;

            BinaryOperation newOp2 = new BitwiseAnd();
            newOp2.RightOperand = exclusiveOr.RightOperand;
            newOp2.LeftOperand = exclusiveOr.LeftOperand;


            list.Add(new Pair(exclusiveOr, newOp1));
            list.Add(new Pair(exclusiveOr, newOp2));

            return this.Visit((BinaryOperation)exclusiveOr);
        }

        public override IExpression Visit(BinaryOperation operation)
        {

            
            //Console.WriteLine("++++++ expression_binaryoperation");
            INamespaceTypeReference typeRef = operation.Type as INamespaceTypeReference;
            if (typeRef == null) return base.Visit(operation);
            if (typeRef.Equals(this.host.PlatformType.SystemInt32))
            {
                //Console.WriteLine(typeRef.ToString());
                CompileTimeConstant newOp1 = new CompileTimeConstant() { Type = this.host.PlatformType.SystemInt32 };
                newOp1.Value = 1;

                CompileTimeConstant newOp2 = new CompileTimeConstant() { Type = this.host.PlatformType.SystemInt32 };
                newOp2.Value = 0;

                CompileTimeConstant newOp3 = new CompileTimeConstant() { Type = this.host.PlatformType.SystemInt32 };
                newOp3.Value = -1;

                list.Add(new Pair(operation, newOp1));
                list.Add(new Pair(operation, newOp2));
                list.Add(new Pair(operation, newOp3));

                UnaryOperation newOp4 = new UnaryNegation();
                newOp4.Operand = operation;
                UnaryOperation newOp5 = new UnaryPlus();
                newOp5.Operand = operation;
                UnaryOperation newOp6 = new OnesComplement();
                newOp6.Operand = operation;

                list.Add(new Pair(operation, newOp4));
                list.Add(new Pair(operation, newOp5));
                list.Add(new Pair(operation, newOp6));

            }
            else if (typeRef.Equals(this.host.PlatformType.SystemBoolean))
            {
                //UnaryOperation newOp7 = new LogicalNot();
                //newOp7.Operand = operation;

                CompileTimeConstant newOp8 = new CompileTimeConstant() { Value = true, Type = this.host.PlatformType.SystemBoolean };


                CompileTimeConstant newOp9 = new CompileTimeConstant() { Value = false, Type = this.host.PlatformType.SystemBoolean };


                //list.Add(new Pair(operation, newOp7));
                list.Add(new Pair(operation, newOp8));
                list.Add(new Pair(operation, newOp9));
            }
            else if (typeRef.Equals(this.host.PlatformType.SystemFloat32))
            {
                CompileTimeConstant newOp10 = new CompileTimeConstant() { Type = this.host.PlatformType.SystemFloat32 };
                newOp10.Value = 1.0;

                CompileTimeConstant newOp11 = new CompileTimeConstant() { Type = this.host.PlatformType.SystemFloat32 };
                newOp11.Value = 0.0;

                CompileTimeConstant newOp12 = new CompileTimeConstant() { Type = this.host.PlatformType.SystemFloat32 };
                newOp12.Value = -1.0;

                list.Add(new Pair(operation, newOp10));
                list.Add(new Pair(operation, newOp11));
                list.Add(new Pair(operation, newOp12));

                UnaryOperation newOp13 = new UnaryNegation();
                newOp13.Operand = operation;
                UnaryOperation newOp14 = new UnaryPlus();
                newOp14.Operand = operation;
                

                list.Add(new Pair(operation, newOp13));
                list.Add(new Pair(operation, newOp14));
             

            }
            else if (typeRef.Equals(this.host.PlatformType.SystemInt16))
            {
                CompileTimeConstant newOp15 = new CompileTimeConstant() { Type = this.host.PlatformType.SystemInt16 };
                newOp15.Value = 1;

                CompileTimeConstant newOp16 = new CompileTimeConstant() { Type = this.host.PlatformType.SystemInt16 };
                newOp16.Value = 0;

                CompileTimeConstant newOp17 = new CompileTimeConstant() { Type = this.host.PlatformType.SystemInt16 };
                newOp17.Value = -1;

                list.Add(new Pair(operation, newOp15));
                list.Add(new Pair(operation, newOp16));
                list.Add(new Pair(operation, newOp17));

                UnaryOperation newOp18 = new UnaryNegation();
                newOp18.Operand = operation;
                UnaryOperation newOp19 = new UnaryPlus();
                newOp19.Operand = operation;
                UnaryOperation newOp20 = new OnesComplement();
                newOp20.Operand = operation;

                list.Add(new Pair(operation, newOp18));
                list.Add(new Pair(operation, newOp19));
                list.Add(new Pair(operation, newOp20));

            }
            else if (typeRef.Equals(this.host.PlatformType.SystemInt64))
            {
                CompileTimeConstant newOp21 = new CompileTimeConstant() { Type = this.host.PlatformType.SystemInt64 };
                newOp21.Value =1;

                CompileTimeConstant newOp22 = new CompileTimeConstant() { Type = this.host.PlatformType.SystemInt64 };
                newOp22.Value = 0;

                CompileTimeConstant newOp23 = new CompileTimeConstant() { Type = this.host.PlatformType.SystemInt64 };
                newOp23.Value = -1;

                list.Add(new Pair(operation, newOp21));
                list.Add(new Pair(operation, newOp22));
                list.Add(new Pair(operation, newOp23));

                UnaryOperation newOp24 = new UnaryNegation();
                newOp24.Operand = operation;
                UnaryOperation newOp25 = new UnaryPlus();
                newOp25.Operand = operation;
                UnaryOperation newOp26 = new OnesComplement();
                newOp26.Operand = operation;

                list.Add(new Pair(operation, newOp24));
                list.Add(new Pair(operation, newOp25));
                list.Add(new Pair(operation, newOp26));

            }
            else if (typeRef.Equals(this.host.PlatformType.SystemInt8))
            {
                CompileTimeConstant newOp27 = new CompileTimeConstant() { Type = this.host.PlatformType.SystemInt8 };
                newOp27.Value = 1;

                CompileTimeConstant newOp28 = new CompileTimeConstant() { Type = this.host.PlatformType.SystemInt8 };
                newOp28.Value = 0;

                CompileTimeConstant newOp29 = new CompileTimeConstant() { Type = this.host.PlatformType.SystemInt8 };
                newOp29.Value = -1;

                list.Add(new Pair(operation, newOp27));
                list.Add(new Pair(operation, newOp28));
                list.Add(new Pair(operation, newOp29));

                UnaryOperation newOp30 = new UnaryNegation();
                newOp30.Operand = operation;
                UnaryOperation newOp31 = new UnaryPlus();
                newOp31.Operand = operation;
                UnaryOperation newOp32 = new OnesComplement();
                newOp32.Operand = operation;

                list.Add(new Pair(operation, newOp30));
                list.Add(new Pair(operation, newOp31));
                list.Add(new Pair(operation, newOp32));

            }
            else if (typeRef.Equals(this.host.PlatformType.SystemFloat64))
            {
                CompileTimeConstant newOp33 = new CompileTimeConstant() { Type = this.host.PlatformType.SystemFloat64 };
                newOp33.Value = 1.0;

                CompileTimeConstant newOp34 = new CompileTimeConstant() { Type = this.host.PlatformType.SystemFloat64 };
                newOp34.Value = 0.0;

                CompileTimeConstant newOp35 = new CompileTimeConstant() { Type = this.host.PlatformType.SystemFloat64 };
                newOp35.Value = -1.0;

                list.Add(new Pair(operation, newOp33));
                list.Add(new Pair(operation, newOp34));
                list.Add(new Pair(operation, newOp35));

                UnaryOperation newOp36 = new UnaryNegation();
                newOp36.Operand = operation;
                UnaryOperation newOp37 = new UnaryPlus();
                newOp37.Operand = operation;


                list.Add(new Pair(operation, newOp36));
                list.Add(new Pair(operation, newOp37));


            }
            return base.Visit(operation);
        }
        
        public override IExpression Visit(UnaryOperation operation)
        {
            //Console.WriteLine("++++++ expression_unaryoperation");

            ITypeReference typeRef = operation.Type as INamespaceTypeReference;
            if (typeRef == null) return operation;
            if (typeRef.Equals(this.host.PlatformType.SystemInt32))
            {


                CompileTimeConstant newOp1 = new CompileTimeConstant();
                newOp1.Value = rand.Next(1, int.MaxValue);

                CompileTimeConstant newOp2 = new CompileTimeConstant();
                newOp2.Value = 0;

                CompileTimeConstant newOp3 = new CompileTimeConstant();
                newOp3.Value = rand.Next(int.MinValue, 0);

                list.Add(new Pair(operation, newOp1));
                list.Add(new Pair(operation, newOp2));
                list.Add(new Pair(operation, newOp3));

                UnaryOperation newOp4 = new UnaryNegation();
                newOp4.Operand = operation;
                UnaryOperation newOp5 = new UnaryPlus();
                newOp5.Operand = operation;
                UnaryOperation newOp6 = new OnesComplement();
                newOp6.Operand = operation;
                list.Add(new Pair(operation, newOp4));
                list.Add(new Pair(operation, newOp5));
                list.Add(new Pair(operation, newOp6));
            }
            else if (typeRef.Equals(this.host.PlatformType.SystemBoolean))
            {
                //UnaryOperation newOp7 = new LogicalNot();
                //newOp7.Operand = operation;

                CompileTimeConstant newOp8 = new CompileTimeConstant() { Value = true, Type = this.host.PlatformType.SystemBoolean };


                CompileTimeConstant newOp9 = new CompileTimeConstant() { Value = false, Type = this.host.PlatformType.SystemBoolean };


                //list.Add(new Pair(operation, newOp7));
                list.Add(new Pair(operation, newOp8));
                list.Add(new Pair(operation, newOp9));
            }

            return base.Visit(operation);

        }
        
        

    }
}